package com.lambert.gingko.watermark.strategy.impl;

import com.lambert.gingko.watermark.annotation.FileProcessor;
import com.lambert.gingko.watermark.enums.FileType;
import com.lambert.gingko.watermark.model.PositionParam;
import com.lambert.gingko.watermark.model.WatermarkConfig;
import com.lambert.gingko.watermark.strategy.AbstWatermarkProcessor;
import lombok.extern.slf4j.Slf4j;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.*;
import java.math.BigDecimal;
import java.util.Iterator;

/**
 * 图片水印处理器（优化版）
 * 功能：为图片添加文字水印，支持自定义位置、旋转角度、透明度和缩放
 * @author lambert
 * @since 2025/08/31
 */
@FileProcessor(FileType.IMAGE)
@Slf4j
public class ImageWatermarkProcessor extends AbstWatermarkProcessor {

    /**
     * 处理图片并添加水印
     * 核心优化：移除重复写入操作，避免二次压缩
     */
    @Override
    protected void doProcess(InputStream is, File outputFile , String extension, WatermarkConfig config) throws Exception {

        try (FileOutputStream os = new FileOutputStream(outputFile)) {
            // 生成带水印的图片
            BufferedImage watermarkedImage = addImageWatermark(is, config, extension);

            // 仅执行一次高质量写入（解决压缩问题）
            writeImageWithQuality(watermarkedImage, extension, os, 1.0f);
        }
    }

    @Override
    public FileType getSupportedType() {
        return FileType.IMAGE;
    }

    /**
     * 为图片添加水印（主流程）
     * 优化点：简化流操作，明确异常传递
     */
    public BufferedImage addImageWatermark(InputStream inputStream, WatermarkConfig config, String format) throws Exception {
        // 使用try-with-resources自动关闭输入流
        try (InputStream sourceIs = inputStream) {
            BufferedImage watermarkImg = createTextWatermarkImage(config);
            BufferedImage sourceImg = ImageIO.read(sourceIs);

            if (sourceImg == null) {
                throw new IOException("无法读取图片流（不支持的格式或文件损坏）");
            }

            // 生成带水印的字节流并转换为BufferedImage
            try (ByteArrayOutputStream watermarkedBaos = markImageWithWatermark(watermarkImg, format, sourceImg, config)) {
                return ImageIO.read(new ByteArrayInputStream(watermarkedBaos.toByteArray()));
            }
        }
    }

    /**
     * 核心水印合成逻辑
     * 优化点：提取绘图配置为独立方法，减少代码嵌套
     */
    private ByteArrayOutputStream markImageWithWatermark(BufferedImage watermarkImg, String format,
                                                         BufferedImage sourceImg, WatermarkConfig config) {
        long startTime = System.currentTimeMillis();
        // try-with-resources自动关闭字节流
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            int sourceWidth = sourceImg.getWidth();
            int sourceHeight = sourceImg.getHeight();
            log.debug("源图尺寸：{}x{}px", sourceWidth, sourceHeight);

            // 初始化绘图上下文（提取为独立方法，减少冗余）
            Graphics2D g = initGraphics2D(sourceImg);

            // 水印旋转（基于源图中心）
            g.rotate(Math.toRadians(config.getAngle()), sourceWidth / 2.0, sourceHeight / 2.0);

            // 水印缩放（仅当水印宽高超过源图时）
            BufferedImage scaledWatermark = scaleWatermarkIfNeeded(watermarkImg, sourceWidth);
            int watermarkWidth = scaledWatermark.getWidth();
            int watermarkHeight = scaledWatermark.getHeight();

            // 计算水印位置
            Point2D.Double position = calculateWatermarkPosition(config, sourceWidth, sourceHeight, watermarkWidth, watermarkHeight);
            log.debug("水印位置：({},{})", position.x, position.y);

            // 绘制水印（设置透明度）
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, config.getOpacity()));
            g.drawImage(scaledWatermark, (int) position.x, (int) position.y, watermarkWidth, watermarkHeight, null);
            g.dispose();

            // 写入带水印的图片（高质量）
            writeImageWithQuality(sourceImg, format, baos, 1.0f);
            log.debug("水印合成耗时：{}ms", System.currentTimeMillis() - startTime);

            return baos;
        } catch (Exception e) {
            log.error("水印合成失败", e);
            throw new RuntimeException("水印合成失败", e); // 明确抛出异常，避免上游误解
        }
    }



    /**
     * 水印缩放（仅当水印宽高超过源图时）
     * 优化点：移除不必要的ImageIcon转换，直接操作BufferedImage
     */
    private BufferedImage scaleWatermarkIfNeeded(BufferedImage watermarkImg, int sourceWidth) throws Exception {
        int watermarkWidth = watermarkImg.getWidth();
        int watermarkHeight = watermarkImg.getHeight();

        if (watermarkWidth <= sourceWidth) {
            return watermarkImg; // 无需缩放
        }

        // 按比例计算缩放后尺寸（保持宽高比）
        double scaleRatio = new BigDecimal((double) sourceWidth / watermarkWidth)
                .setScale(4, BigDecimal.ROUND_HALF_UP).doubleValue();
        int scaledWidth = sourceWidth;
        int scaledHeight = (int) (watermarkHeight * scaleRatio);

        log.debug("水印缩放前：{}x{}px，缩放后：{}x{}px（比例：{}）",
                watermarkWidth, watermarkHeight, scaledWidth, scaledHeight, scaleRatio);

        return scaleImage(watermarkImg, scaledWidth, scaledHeight);
    }

    /**
     * 图片缩放（通用方法）
     * 优化点：移除临时PNG转换，直接通过AffineTransformOp缩放，避免质量损失
     */
    private BufferedImage scaleImage(BufferedImage sourceImage, int targetWidth, int targetHeight) {
        // 计算缩放比例
        double scaleX = (double) targetWidth / sourceImage.getWidth();
        double scaleY = (double) targetHeight / sourceImage.getHeight();
        log.debug("缩放比例 - 宽：{}，高：{}", scaleX, scaleY);

        // 使用仿射变换进行平滑缩放
        AffineTransform transform = AffineTransform.getScaleInstance(scaleX, scaleY);
        AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR);

        // 直接返回缩放后的BufferedImage（无需中间流转换）
        return op.filter(sourceImage, new BufferedImage(targetWidth, targetHeight, sourceImage.getType()));
    }

    /**
     * 计算水印位置
     * 优化点：明确参数含义，使用Point2D.Double提高精度
     */
    private Point2D.Double calculateWatermarkPosition(WatermarkConfig config, int sourceWidth, int sourceHeight,
                                                      int watermarkWidth, int watermarkHeight) {
        PositionParam positionParam = config.getPositionParam();
        positionParam.setEnclosingWidth(sourceWidth);
        positionParam.setEnclosingHeight(sourceHeight);
        positionParam.setWidth(watermarkWidth);
        positionParam.setHeight(watermarkHeight);

        Point point = config.getPosition().calculate(positionParam);
        return new Point2D.Double(point.x, point.y);
    }



    /**
     * 高质量写入图片（核心工具方法）
     * 优化点：确保ImageWriter资源释放，增强格式兼容性
     */
    private void writeImageWithQuality(BufferedImage image, String format, OutputStream outputStream, float quality) throws IOException {
        Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(format);
        if (!writers.hasNext()) {
            throw new IllegalArgumentException("不支持的图片格式：" + format);
        }

        ImageWriter writer = writers.next();
        ImageWriteParam writeParam = writer.getDefaultWriteParam();

        try (ImageOutputStream imageOut = ImageIO.createImageOutputStream(outputStream)) {
            // 对支持压缩的格式（如JPG）设置高质量参数
            if (writeParam.canWriteCompressed()) {
                writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
                writeParam.setCompressionQuality(quality);
            }

            writer.setOutput(imageOut);
            writer.write(null, new IIOImage(image, null, null), writeParam);
        } finally {
            writer.dispose(); // 确保释放ImageWriter资源
        }
    }
}